package com.itheima.stock.service.impl;

import com.alibaba.excel.EasyExcel;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.gson.Gson;
import com.itheima.stock.common.domain.*;
import com.itheima.stock.config.StockInfoConfig;
import com.itheima.stock.mapper.StockBlockRtInfoMapper;
import com.itheima.stock.mapper.StockBusinessMapper;
import com.itheima.stock.mapper.StockMarketIndexInfoMapper;
import com.itheima.stock.mapper.StockRtInfoMapper;
import com.itheima.stock.pojo.StockBusiness;
import com.itheima.stock.service.StockService;
import com.itheima.stock.utils.DateTimeUtil;
import com.itheima.stock.vo.resp.PageResult;
import com.itheima.stock.vo.resp.R;
import com.itheima.stock.vo.resp.ResponseCode;
import com.itheima.stock.vo.resp.StockBlockRtInfoResp;
import org.apache.logging.log4j.util.Strings;
import org.joda.time.DateTime;
import org.joda.time.DateTimeUtils;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author by itheima
 * @Date 2021/12/19
 * @Description
 */
@Service("stockService")
public class StockServiceImpl implements StockService {
    // 主营业务
    @Autowired
    private StockBusinessMapper stockBusinessMapper;
    //股票大盘数据
    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;
    // yml中的大盘code
    @Autowired
    private StockInfoConfig stockInfoConfig;
    //股票板块详情信息
    @Autowired
    private StockBlockRtInfoMapper stockBlockRtInfoMapper;
    // 个股
    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;

    @Override
    public List<StockBusiness> getAllStockBusiness() {
        return stockBusinessMapper.getAll();
    }

    /**
     * 获取国内大盘的实时数据
     *
     * @return
     */
    @Override
    public R<List<InnerMarketDomain>> innerIndexAll() {
        //1.获取国内大盘的id集合
        List<String> innerIds = stockInfoConfig.getInner();
        //2.获取最近最新的股票有效交易日期
        DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        // 3 将上述的时间转成java中的date
        Date date = lastDateTime.toDate();
/*        //TODO:后续大盘数据实时拉去，将该行注释掉 传入的日期秒必须为0
        //伪数据测试
        String mockDate = "20211226105600";
        date = DateTime.parse(mockDate, DateTimeFormat.forPattern("yyyyMMddHHmmss")).toDate();*/
        //4.调用mapper查询指定日期下对应的国内大盘数据
        List<InnerMarketDomain> list = stockMarketIndexInfoMapper.selectByIdsAndDate(innerIds, date);
        //5 返回查询结果
        if (CollectionUtils.isEmpty(list)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(list);
    }

    /**
     * 需求说明: 沪深两市板块分时行情数据查询，以交易时间和交易总金额降序查询，取前10条数据
     *
     * @return
     */
    @Override
    public R<List<StockBlockRtInfoResp>> sectorAllLimit() {
        //1.调用mapper接口获取数据 TODO 优化 避免全表查询 根据时间范围查询，提高查询效率
        /*
            // 测试伪数据
            // date-> datetime
            String s = "2021-12-21 09:30:00";
            DateTime dateTime = DateTime.parse(s, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
            // 获取最新股票的有效时间
            DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(dateTime);
            // dateTime->date
            Date curDate = lastDateTime.toDate();
        */
        // 获取最新的股市更新时间
        DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        // 转成Date类型
        Date curDate = lastDateTime.toDate();
        List<StockBlockRtInfoResp> infos = stockBlockRtInfoMapper.sectorAllLimit(curDate);
        //2.组装数据
        if (CollectionUtils.isEmpty(infos)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(infos);
    }

    /**
     * 沪深两市个股涨幅分时行情数据查询，以时间顺序和涨幅查询前10条数据
     *
     * @return
     */
    @Override
    public R<List<StockUpdownDomain>> stockIncreaseLimit() {
        //  1 获取最新的股市更新时间
        DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        // 转成Date类型
        Date curDate = lastDateTime.toDate();
        // 2 调用mapper层方法
/*        //伪数据
        String mockStr = "2021-12-27 09:47:00";
        curDate = DateTime.parse(mockStr, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();*/
        List<StockUpdownDomain> infos = stockRtInfoMapper.stockIncreaseLimit(curDate);
        // 判断查询结果是否为null
        if (CollectionUtils.isEmpty(infos)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        // 返回数据
        return R.ok(infos);
    }

    /**
     * 沪深两市个股行情列表查询 ,以时间顺序和涨幅分页查询
     *
     * @param page     当前页
     * @param pageSize 每页大小
     * @return
     */
    @Override
    public R<PageResult<StockUpdownDomain>> getRtStockByPage(Integer page, Integer pageSize) {
        // 1 设置分页参数 (yml中配置了分页助手)
        // 类似aop思想 给下面的mapper层方法进行sql语句最后添加 limit语句的增强
        PageHelper.startPage(page, pageSize);
        // 2 调用mapper层方法
        List<StockUpdownDomain> list = stockRtInfoMapper.getRtStockAll();
        // 3 判断是否有数据
        if (CollectionUtils.isEmpty(list)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        // 4 有则封装数据
        // 4.1 封装分页数据
        PageInfo<StockUpdownDomain> info = new PageInfo<>(list);
        // 4.2 封装pageResult
        PageResult<StockUpdownDomain> pageResult = new PageResult<>(info);
        // 5 返回对象
        return R.ok(pageResult);
    }

    /**
     * 功能描述：沪深两市涨跌停分时行情数据查询，查询T日每分钟的涨跌停数据（T：当前股票交易日）
     * 查询每分钟的涨停和跌停的数据的同级；
     * 如果不在股票的交易日内，那么就统计最近的股票交易下的数据
     * map:
     * upList:涨停数据统计
     * downList:跌停数据统计
     *
     * @return
     */
    @Override
    public R<Map> upDownCount() {
        // 1 获取最新股市的更新时间
        DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        // 2 获取最新股市的开盘时间
        Date openDate = DateTimeUtil.getOpenDate(lastDateTime).toDate();
        // 3 获取最新股市的收盘时间
        Date closeDate = DateTimeUtil.getCloseDate(lastDateTime).toDate();
/*        // 4 测试的伪数据
        //TODO mock_data 后续数据实时获取时，注释掉
        openDate = DateTime.parse("2021-12-19 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        closeDate = DateTime.parse("2021-12-19 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();*/
        // 5 调用mapper层方法
        List<Map> upCountList = stockRtInfoMapper.getStockUpDownCount(openDate, closeDate, 1);
        List<Map> downCountList = stockRtInfoMapper.getStockUpDownCount(openDate, closeDate, 0);
        // 6 封装数据
        Map<String, List<Map>> map = new HashMap<>();
        map.put("upList", upCountList);
        map.put("downList", downCountList);
        return R.ok(map);
    }

    /**
     * 将指定页的股票数据导出到excel表下
     *
     * @param response
     * @param page     当前页
     * @param pageSize 每页大小
     */
    @Override
    public void stockExport(HttpServletResponse response, Integer page, Integer pageSize) {
        try {
            //1.设置响应数据的类型:excel
            response.setContentType("application/vnd.ms-excel");
            //2.设置响应数据的编码格式
            response.setCharacterEncoding("utf-8");
            //3.设置默认的文件名称
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode("stockRt", "UTF-8");
            //设置默认文件名称
            response.setHeader("content-disposition", "attachment;filename=" + fileName + ".xlsx");
            // 4 分页查询股票数据
            // 4.1 设置分页参数
            PageHelper.startPage(page, pageSize);
            // 4.2 查询
            List<StockUpdownDomain> infos = stockRtInfoMapper.getRtStockAll();
            Gson gson = new Gson();
            //4.3 判断查询结果是否为空
            if (CollectionUtils.isEmpty(infos)) {
                // 为空则报错且结束程序
                R<String> error = R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
                String json = gson.toJson(error);
                response.getWriter().write(json);
                return;
            }
            // 5 将List<StockUpdownDomain> 转化成List<StockExcelDomain>
            List<StockExcelDomain> domains = infos.stream().map(item -> {
                StockExcelDomain domain = new StockExcelDomain();
                BeanUtils.copyProperties(item, domain);
                return domain;
            }).collect(Collectors.toList());
            // 6 导出
            EasyExcel.write(response.getOutputStream(), StockExcelDomain.class).sheet("股票数据").doWrite(domains);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 功能描述：统计国内A股大盘T日和T-1日成交量对比功能（成交量为沪市和深市成交量之和）
     * map结构示例：
     * {
     * "volList": [{"count": 3926392,"time": "202112310930"},......],
     * "yesVolList":[{"count": 3926392,"time": "202112310930"},......]
     * }
     *
     * @return
     */
    @Override
    public R<Map> stockTradeVolInnerMarket() {
        // 1 获取最新大盘数据更新时间
        DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date lastDate = lastDateTime.toDate();
        // 2 获取最近更新日的开盘时间
        Date openDate = DateTimeUtil.getOpenDate(lastDateTime).toDate();
/*        //TODO 后续注释掉 mock-data
        String tDateStr="20220103143000";
        lastDate = DateTime.parse(tDateStr, DateTimeFormat.forPattern("yyyyMMddHHmmss")).toDate();
        String openDateStr="20220103093000";
        openDate = DateTime.parse(openDateStr, DateTimeFormat.forPattern("yyyyMMddHHmmss")).toDate();*/
        // 3 获取最近更新日的前一日时间
        DateTime preLastDateTime = DateTimeUtil.getPreviousTradingDay(lastDateTime);
        Date preLastDate = preLastDateTime.toDate();
        // 4 获取更新日前一日的开盘时间
        Date preOpenDate = DateTimeUtil.getOpenDate(preLastDateTime).toDate();
/*        //TODO 后续注释掉 mock-data
        String preTStr="20220102143000";
        preLastDate = DateTime.parse(preTStr, DateTimeFormat.forPattern("yyyyMMddHHmmss")).toDate();
        String openDateStr2="20220102093000";
        preOpenDate= DateTime.parse(openDateStr2, DateTimeFormat.forPattern("yyyyMMddHHmmss")).toDate();*/
        // 5 调用mapper层方法
        /*
            参数: 1 国内A股大盘的code集合
                 2 查询日期对应的开盘时间
                 3 查询日期对应的 DateTime.now() 的时间
         */
        // 获取国内A股大盘ID
        List<String> innerCode = stockInfoConfig.getInner();
        List<Map> dateVol = stockMarketIndexInfoMapper.stockVolCountByTime(innerCode, openDate, lastDate);
        List<Map> preDateVol = stockMarketIndexInfoMapper.stockVolCountByTime(innerCode, preOpenDate, preLastDate);
        // 6 判断
        if (CollectionUtils.isEmpty(dateVol)) {
            dateVol = new ArrayList<>();
        }
        if (CollectionUtils.isEmpty(preDateVol)) {
            preDateVol = new ArrayList<>();
        }
        // 7 封装数据
        HashMap<String, List<Map>> data = new HashMap<>();
        data.put("volList", dateVol);
        data.put("yesVolList", preDateVol);
        // 8 返回数据
        return R.ok(data);
    }

    /**
     * 查询当前时间下股票的涨跌幅度区间统计功能
     * 如果当前日期不在有效时间内，则以最近的一个股票交易时间作为查询点
     *
     * @return
     */
    @Override
    public R<Map> stockUpDownRegionCount() {
        // 1 先获取最近的交易期
        DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date lastDate = lastDateTime.toDate();
/*        //TODO 后续删除 mock-data
        String  mockDate="20220106095500";
        lastDate = DateTime.parse(mockDate, DateTimeFormat.forPattern("yyyyMMddHHmmss")).toDate();*/
        // 2 调用mapper层的方法
        List<Map> infos = stockRtInfoMapper.stockUpDownRegionCount(lastDate);
        // 3 获取股票涨幅区间的集合
        List<String> upDownRange = stockInfoConfig.getUpDownRange();
        // 4 遍历集合
        // 新建一个集合作为数据的存储
        List<Map> newMap = new ArrayList<>();
        // 遍历查询匹配的 区间
        for (String rate : upDownRange) {
            // 新建一个临时map存储查询到的数据
            Map temp = null;
            for (Map info : infos) {
                // 根据YML配置的区间,一一匹配 进行赋值
                if (rate.equals(info.get("title"))) {
                    temp = info;
                }
            }
            // 判断上述赋值后是否为空,为空就封装一个空的集合
            if (temp == null) {
                temp = new HashMap();
                temp.put("title", rate);
                temp.put("count", 0);
            }
            newMap.add(temp);
        }
        // 5 封装数据
        HashMap<String, Object> data = new HashMap<>();
        data.put("time", lastDateTime.toString("yyyy-MM-dd HH:mm:ss"));
        data.put("infos", newMap);
        //返回响应数据
        return R.ok(data);
    }

    /**
     * 功能描述：查询单个个股的分时行情数据，也就是统计指定股票T日每分钟的交易数据；
     * 如果当前日期不在有效时间内，则以最近的一个股票交易时间作为查询时间点
     *
     * @param stockCode 股票编码
     * @return
     */
    @Override
    public R<List<Stock4MinuteDomain>> stockScreenTimeSharing(String stockCode) {
        // 1 获取股市最近的更新时间
        DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date lastDate = lastDateTime.toDate();
        // 2 获取开盘时间
        Date openDate = DateTimeUtil.getOpenDate(lastDateTime).toDate();
/*        //TODO 后续删除 mock-data
        String mockDate="20220106142500";
        lastDate=DateTime.parse(mockDate, DateTimeFormat.forPattern("yyyyMMddHHmmss")).toDate();
        String openDateStr="20220106093000";
        openDate=DateTime.parse(openDateStr, DateTimeFormat.forPattern("yyyyMMddHHmmss")).toDate();*/
        // 3 调用mapper层方法
        List<Stock4MinuteDomain> list = stockRtInfoMapper.getStockEveryTimeInfo(stockCode, openDate, lastDate);
        // 4 返回数据
        if(CollectionUtils.isEmpty(list)){
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(list);
    }

    /**
     * 单个个股日K 数据查询 ，可以根据时间区间查询数日的K线数据
     *
     * @param stockCode 股票编码
     */
    @Override
    public R<List<Stock4EvrDayDomain>> stockCreenDkLine(String stockCode) {
        // 1 获取股市最近的更新时间
        DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date lastDate = lastDateTime.toDate();
        // 2 默认获取近20天的数据 那么时间往前推20天
        DateTime startDateTime = lastDateTime.minusDays(20);
        Date startDate = startDateTime.toDate();
/*        // TODO 伪数据后续删除
        String avlDate="20220106142500";
        lastDate=DateTime.parse(avlDate, DateTimeFormat.forPattern("yyyyMMddHHmmss")).toDate();
        String openDate="20220101093000";
        startDate=DateTime.parse(openDate, DateTimeFormat.forPattern("yyyyMMddHHmmss")).toDate();*/
        // 3 调用mapper层方法
        List<Stock4EvrDayDomain> info = stockRtInfoMapper.getStockEveryDayInfo(stockCode, startDate, lastDate);
        // 4 返回查询结果
        if(CollectionUtils.isEmpty(info)){
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(info);
    }
        /*
        功能描述：外盘指数行情数据查询，根据时间和大盘点数降序排序取前4
        服务路径：/api/quot/external/index
        服务方法：GET
        请求参数：无
     */
    @Override
    public R<List<OutMarketDomain>> getOutMarketAllInfo() {
        //1.获取外盘的id集合
        List<String> outerIds = stockInfoConfig.getOuter();
        //2.获取最近最新的股票有效交易日期
        DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        // 3 将上述的时间转成java中的date
        Date date = lastDateTime.toDate();
        //伪数据测试
        String mockDate = "20211226120000";
        date = DateTime.parse(mockDate, DateTimeFormat.forPattern("yyyyMMddHHmmss")).toDate();
        //4.调用mapper查询指定日期下对应的国内大盘数据
        List<OutMarketDomain> list = stockMarketIndexInfoMapper.getOutMarketAllInfo(outerIds, date);
        //5 返回查询结果
        if (CollectionUtils.isEmpty(list)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(list);
    }
    /*
        功能描述：根据输入的个股代码，进行模糊查询，返回证券代码和证券名称
        服务路径：/quot/stock/search
        服务方法：GET
        请求参数：searchStr （只接受代码模糊查询，不支持文字查询）
     */
    @Override
    public R<List<QueryLikeDomain>> queryLikeByCode(String searchStr) {
        // 1 调用mapper层方法
        List<QueryLikeDomain> list = stockBusinessMapper.queryLikeByCode(searchStr);
        // 2 判断并返回结果
        if (CollectionUtils.isEmpty(list)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(list);
    }
    /*
        功能描述：个股主营业务查询接口
        服务路径：/api/quot/stock/describe
        服务方法：GET
        请求参数：code #股票编码
     */
    @Override
    public R<StockRtDescriptionDomain> queryRtStockBusiness(String code) {
        // 1 调用mapper层方法
        StockRtDescriptionDomain result = stockBusinessMapper.queryRtStockBusiness(code);
        // 2 判断并返回结果
        if(result == null){
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(result);
    }
        /*
        功能描述：统计每周内的股票数据信息，信息包含：
        股票ID、 一周内最高价、 一周内最低价 、周1开盘价、周5的收盘价、
        整周均价、以及一周内最大交易日期（一般是周五所对应日期）;
        服务路径：/api/quot/stock/screen/weekkline
        服务方法：GET
        请求参数：code //股票编码
     */
    @Override
    public R<List<WeeklineDomain>> getRtStockWeekline(String code) {
        // 1 调用mapper层方法
        List<WeeklineDomain> result = stockRtInfoMapper.getRtStockWeekline(code);
        // 2 判断并返回结果
        if(CollectionUtils.isEmpty(result)){
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(result);
    }
        /*
        功能描述：
        获取个股最新分时行情数据，主要包含：
        开盘价、前收盘价、最新价、最高价、最低价、成交金额和成交量、交易时间信息;
        服务路径：/api/quot/stock/screen/second/detail
        服务方法：GET
        请求参数：code //股票编码
        请求频率：每分钟
     */
    @Override
    public R<StockRtMinuteDomain> getStockRtMinuteInfo(String code) {
        // 1 获取最近的股市更新时间
        DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date lastDate = lastDateTime.toDate();
        // TODO:伪数据
        String avlDate="20220106142500";
        lastDate=DateTime.parse(avlDate, DateTimeFormat.forPattern("yyyyMMddHHmmss")).toDate();
        // 2 调用mapper层方法
        StockRtMinuteDomain result = stockRtInfoMapper.getStockRtMinuteInfo(code,lastDate);
        // 3 判断并返回结果
        if(result == null){
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(result);
    }
        /*
        功能描述：个股交易流水行情数据查询--查询最新交易流水，按照交易时间降序取前10
        服务路径：/quot/stock/screen/second
        服务方法：GET
        请求频率：5秒
     */
    @Override
    public R<List<StockRtLastTradeInfo>> queryStockRtLastTradeInfo() {
        // 1 调用mapper层方法
        List<StockRtLastTradeInfo> result = stockRtInfoMapper.queryStockRtLastTradeInfo();
        // 2 判断并返回结果
        if(CollectionUtils.isEmpty(result)){
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(result);
    }
}